package src.model.gameObject.ability;

import src.event.events.ConsoleUpdateEvent;
import src.model.gameObject.*;
import src.model.gameObject.eventInterfaces.MovePublisher;
import src.model.gameWorld.ActualMap;
import src.model.gameWorld.PlayerLookupTable;
import src.model.gameWorld.Position;
import src.model.gameWorld.pathfinding.Path;
import src.model.gameWorld.pathfinding.astar.AStarPathfinder;
import src.view.ViewEventHandler;

/**
 * <b>Overview</b><br>
 * <p>This is in charge of moving PlayerGameObjects. It moves a PlayerGameObject from one tile to another,
 * and it uses A* algorithm to find the path.</p>
 * <br><br>
 * <b>Responsibilites</b><br>
 * <p>Move the PlayerGameObject in short fashion</p>
 * <br><br>
 * <b>Collaborators</b><br>
 * AStarPathFinder - calculates the path.<br>
 * ActualMap - to move the PGO.<br>
 * <br>
 * <b>Implementor</b> - Tristan Lohman<br>
 * <b>Tester</b> - Tim<br>
 * @author Tristan
 *
 */
public class Move extends ActiveAbility implements MovePublisher{

	private ActualMap am;
	/**
	 * Constructs a new Move ability
	 * @param pgo The PlayerGameObject that owns this.
	 * @param playerLookup The PlayerLookupTable for this ability.
	 * @param am The ActualMap to reference.
	 */
	public Move(PlayerGameObject pgo, PlayerLookupTable playerLookup, ActualMap am) {
		super(pgo, AbilityType.MOVE, playerLookup);
		this.am = am;
	}

	/**
	 * This is move in all its glory. This uses the A* algorithm to find the shortest path between two points.
	 * It takes the final destination to move to as the parameter.
	 * @param position The position to move to.
	 * @return SUCCESS if the move completed. IMPOSSIBLE if move cannot complete (impassable terrain, unit in the way).
	 * FAILED if movement ran out, or move is reasonably expected to resume next turn.
	 */
	public AbilityReturnResult execute(Position... position){

		ViewEventHandler.fireEvent(new ConsoleUpdateEvent("Avast me hearties, prepare to set sail!"));

		Path movers = null;
		try {movers = AStarPathfinder.makePath(super.gameObject(), position[0]);}
		catch(Exception e) {return AbilityReturnResult.IMPOSSIBLE;}

		if (movers == null)
		{
			ViewEventHandler.fireEvent(new ConsoleUpdateEvent("Ahoy! There be somethin' in da way!"));
			return AbilityReturnResult.IMPOSSIBLE;
		}

		Position moveTo = movers.next();
		moveTo = movers.next();

		GameObject terrain = am.findObject(moveTo, GameObjectType.TERRAIN);
		Ability passable = null;
		if(terrain != null)
			passable = terrain.getAbility(AbilityType.PASSABLE);

		if (passable == null)
		{
			ViewEventHandler.fireEvent(new ConsoleUpdateEvent("Ahoy! There be somethin' in da way!"));
			return AbilityReturnResult.IMPOSSIBLE;
		}

		float cost = passable.getAttribute(AbilityAttributes.STRENGTH);
		if (!super.hasActionPoints((int)cost))
		{
			ViewEventHandler.fireEvent(new ConsoleUpdateEvent("Arr, there be no more wind in da sails..."));
			return AbilityReturnResult.FAILIED;
		}

		if (am.moveObject(super.gameObject(), moveTo)) {
			ViewEventHandler.fireEvent(new ConsoleUpdateEvent(gameObject().getSubtype().toString().toLowerCase() + " sailed to " + moveTo));
			super.useActionPoint((int)cost);
			publishMove(super.gameObject(), moveTo);
		}

		if (position[0].equals(moveTo)) return AbilityReturnResult.SUCCESS;
		return execute(position);
	}

	public void publishMove(GameObject go, Position p){
		src.model.gameObject.eventBrokers.MoveEventBroker.recognizeMove(go, p);
	}

	public boolean queueToMission(Position...position){
		if (position.length == 0)  return false;
		super.gameObject().enqueueAbility(this, position);
		return true;
	}

	public void destroySub(){
		this.am = null;
	}
}
